#include "Bot.h"
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <math.h>

using namespace std;

//constructor
Bot::Bot()
{
    srand(time(NULL));
}

//plays a single game of Ants.
void Bot::playGame()
{
    //reads the game parameters and sets up
    cin >> state;
    state.setup();
    endTurn();

    //continues making moves while the game is not over
    while (cin >> state)
    {
        state.updateVisionInformation();
        makeMoves();
        endTurn();
    }
}

bool Bot::isGatherer(Location ant)
{
    for( unsigned int i=0; i < gatherers.size(); ++i )
    {
        if( (gatherers[i].row == ant.row) && (gatherers[i].col == ant.col) )
        {
            return true;
        }
    }

    return false;
}

void Bot::sendGatherers()
{
    for (unsigned int fid = 0; fid < state.food.size(); ++fid)
    {
        int dirToFood;
        Location gatherer = foodBFS( state.food[fid], &dirToFood );

        if( dirToFood == -1 ) continue; // skip

        if( isGatherer( gatherer ) ) continue; // skip

        if( !canMakeMove( state.getLocation(gatherer, dirToFood) ) ) continue;

        gatherers.push_back( gatherer );
        move( gatherer,  dirToFood );
    }
}

//makes the bots moves for the turn
void Bot::makeMoves()
{
    state.bug << "turn " << state.turn << ":" << endl;
    state.bug << state << endl;

    for( unsigned int a=0; a<state.myAnts.size(); ++a )
    {
        myFutureAnts.push_back( state.myAnts[a] );
    }

    //send the ants to the closest foods
    sendGatherers();

    //picks out moves for each ant
    for (int ant = 0; ant < (int) state.myAnts.size(); ant++)
    {
        Location src = state.myAnts[ant];

        // if its a gatherer, skip it
        if( isGatherer(src) ) continue;

        //else, move randomly
        moveRandom(ant);
    }

    state.bug << "time taken: " << state.timer.getTime() << "ms" << endl
            << endl;
}
;

//finishes the turn
void Bot::endTurn()
{
    gatherers.clear();
    myFutureAnts.clear();

    if (state.turn > 0)
        state.reset();
    state.turn++;

    cout << "go" << endl;
}
;

bool Bot::canMakeMove(Location loc)
{
    for (unsigned int a = 0; a < myFutureAnts.size(); ++a)
    {
        if (loc.row == myFutureAnts[a].row && loc.col == myFutureAnts[a].col)
            return false;
    }

    if (state.grid[loc.row][loc.col].isWater)
        return false;

    /*for (unsigned int e = 0; e < state.enemyAnts.size(); ++e)
    {
        if (abs(state.distance(state.enemyAnts[e], loc)) < 3)
            return false;
    }*/

    return true;
}

void Bot::moveRandom(int ant)
{
    int d = rand() % TDIRECTIONS;
    Location loc = state.getLocation(state.myAnts[ant], d);
    int count = 0;
    const int maxCount = 5;

    while (!canMakeMove(loc) && count < maxCount)
    {
        count++;
        d = rand() % TDIRECTIONS;
        loc = state.getLocation(state.myAnts[ant], d);
    }

    if (count < maxCount)
    {
        move(state.myAnts[ant], d);
    }
}

void Bot::move(Location loc, int dir)
{
    // get destination location
    Location dst = state.getLocation(loc, dir);

    // move if ok to move
    if (canMakeMove(dst))
    {
        myFutureAnts.push_back(dst);
        state.makeMove(loc, dir);
    }
}

Location Bot::foodBFS(Location foodLoc, int* dir)
{
    std::queue<Location> queue;
    memset(mark, 0, sizeof(mark));

    mark[foodLoc.row][foodLoc.col] = 1;
    queue.push(foodLoc);

    while (!queue.empty())
    {
        Location current = queue.front();
        queue.pop();

        for (int d = 0; d < TDIRECTIONS; ++d)
        {
            Location n = state.getLocation(current, d);

            if (state.grid[n.row][n.col].ant == 0)
            {
                if( !isGatherer( n ) )
                {
                    *dir = (d + 2) % 4;
                    return n;
                }
            }

            if (!mark[n.row][n.col] &&
                !state.grid[n.row][n.col].isWater     )
            {
                mark[n.row][n.col] = 1;
                queue.push(n);
            }
        }
    }
    Location noLocation( -1, -1 );
    *dir = -1;
    return noLocation;
}
